import numpy as np
"""
--------------------------------向量操作类（numpy实现）--------------------------------
方法1. 向量内积       @
方法2. 按元素相乘     *
方法3. 按元素相加     +
方法4. 向量和标量相乘  *
注意：VectorOp的方法均是ndarray变量
"""
class VectorOp(object):
    '''
    @staticmethod申明该函数在类即使没有实例化也能够使用该函数
    '''
    #内积
    @staticmethod 
    def dot(x,y): #x,y为ndarray
        return np.array(x).dot(np.array(y))

    #元素相乘
    @staticmethod
    def element_multiply(x,y):
        return np.array(x)*np.array(y)

    #元素相加
    @staticmethod
    def element_add(x,y): 
        return np.array(x)+np.array(y)

    #向量与标量相乘
    @staticmethod
    def scale_multiply(v,s):
        return np.array(v)*s
"""
--------------------------------perceptron类--------------------------------
"""
class perceptron(object):
    #初始化感知机，设置输入参数的个数以及激活函数类型
    def __init__(self,input_num,activator):
        self.activator = activator
        self.weights = [0.0]*input_num
        self.bias = 0.0
    #打印学习到的权重、偏置项
    def __str__(self):
        return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias)
    

    #训练
    def train(self,input_vec,labels,iteration,rate):
        for i in range(iteration):
            self.one_iteration(input_vec,labels,rate)

    #一次迭代
    def one_iteration(self,input_vec,labels,rate):
        samples = zip(input_vec,labels) #对应索引组合在一起，生成新的元组
        for (input_vec,labels) in samples : #一步随机梯度下降法
            output = self.predict(input_vec)
            self.update_weights(input_vec,output,labels,rate)

    #输入数据，计算感知机的输出结果
    def predict(self,input_vec):
        return self.activator(VectorOp.dot(input_vec, self.weights)+self.bias)

    #更新参数       
    def update_weights(self,input_vec,output,label,rate):
        delta = label - output
        self.weights = VectorOp.element_add(self.weights, VectorOp.scale_multiply(input_vec, delta*rate))
        self.bias += delta*rate


#define activator function
def f(x):
    return 1 if x >0 else 0

def get_training_dataset():
    """
    基于and真值表构建训练数据
    """
    # 构建训练数据
    # 输入向量列表
    input_vecs = [[2, 3], [3, 3], [2, 2], [3, 2],[1,3],[0,3],[0,2]]
    # 期望的输出列表，注意要与输入一一对应
    # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0
    labels = [1, 1, 0, 0, 1, 1, 0]
    return input_vecs, labels

def train_and_perceptron():
    """
    使用and真值表训练感知器
    """
    # 创建感知器，输入参数个数为2（因为and是二元函数），激活函数为f
    p = perceptron(2, f)
    # 训练，迭代10轮, 学习速率为0.1
    input_vecs, labels = get_training_dataset()
    p.train(input_vecs, labels, 50, 0.1)
    # 返回训练好的感知器
    return p

if __name__ == '__main__':
    # 训练and感知器
    and_perception = train_and_perceptron()
    # 打印训练获得的权重
    print(and_perception)
    # 测试
    print('1 and 1 = %d' % and_perception.predict([1, 1]))
    print('0 and 0 = %d' % and_perception.predict([0, 0]))
    print('1 and 0 = %d' % and_perception.predict([1, 0]))
    print('0 and 1 = %d' % and_perception.predict([0, 1]))
    print('5 and 5 = %d' % and_perception.predict([5, 5]))